package proto

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"lark/util"
	"math"
	"net"
	"strconv"
	"time"
)

const (
	lengthOfTag    = 2
	lengthOflength = 4
	lengthOfHeader = lengthOfTag + lengthOflength
)

type ProtoKind int

const (
	ProtoKindJmtext ProtoKind = iota + 1
	ProtoKindThrift
	ProtoKindLark
)

var (
	ErrSegment = errors.New("error segment")
)

type TLVParser struct {
	buf  []byte
	r    net.Conn
	sbuf []byte
}

func NewTLVParser(r net.Conn) *TLVParser {
	return &TLVParser{
		buf:  make([]byte, 32),
		sbuf: make([]byte, 32),
		r:    r,
	}

}

// 3\nRPC\n  刚好六个字符
func (p *TLVParser) RecvHeader(d time.Duration) error {
	var deadline time.Time
	if d != 0 {
		deadline = time.Now().Add(d)
	}

	p.r.SetReadDeadline(deadline)

	//读取 tag 和 length
	_, err := io.ReadFull(p.r, p.buf[:lengthOfHeader])
	if err != nil {
		return err
	}

	return nil
}

func (p *TLVParser) Kind() ProtoKind {
	if bytes.Contains(p.buf[:lengthOfHeader], []byte("RPC")) {
		return ProtoKindJmtext
	} else if bytes.Equal(p.buf[:lengthOfTag], []byte{27, 88}) && !bytes.Equal(p.buf[4:6], []byte{128, 1}) {
		return ProtoKindLark
	} else {
		return ProtoKindThrift
	}
}

func (p *TLVParser) RecvJMTextBody() ([]byte, error) {
	br := bufio.NewReader(p.r)
	line, prefix, err := br.ReadLine()
	if err != nil {
		return nil, err
	}

	if prefix {
		return nil, errors.New("line prefix too long")
	}

	l, err := strconv.Atoi(string(line))
	if err != nil {
		return nil, err
	}

	//有的客户端最后有一个 \n 有的没有，此处不读取最后一个 \n ，必须使用短连接
	var b = make([]byte, l+lengthOfHeader+len(line)+1)
	copy(b, p.buf[:lengthOfHeader])
	copy(b[lengthOfHeader:], line)
	b[lengthOfHeader+len(line)] = 10

	_, err = io.ReadFull(br, b[lengthOfHeader+len(line)+1:])
	if err != nil {
		return nil, err
	}

	//尝试最后一个\n 后才能关闭链接
	p.r.SetDeadline(time.Now().Add(time.Microsecond * 100))
	br.Read(make([]byte, 1))

	return b, nil
}

func (p *TLVParser) RecvThriftBody() ([]byte, error) {
	l := binary.BigEndian.Uint32(p.buf[:4])

	var b = make([]byte, l+4)
	_, err := io.ReadFull(p.r, b[6:])
	if err != nil {
		return nil, err
	}

	copy(b[:6], p.buf[:lengthOfHeader])
	return b, nil
}

func (p *TLVParser) RecvBody(d time.Duration) (uint16, [][]byte, error) {
	tag := p.rtag()
	l := p.rlen()
	p.ensureLen(l)

	var deadline time.Time
	if d != 0 {
		deadline = time.Now().Add(d)
	}

	p.r.SetReadDeadline(deadline)
	_, err := io.ReadFull(p.r, p.buf[lengthOfHeader:lengthOfHeader+l])
	if err != nil {
		return 0, nil, err
	}

	//解析 value
	var value [][]byte
	var offset uint32 = lengthOfHeader
	for offset < l+lengthOfHeader {
		ll := binary.BigEndian.Uint32(p.buf[offset : offset+lengthOflength])
		value = append(value, p.buf[offset+lengthOflength:offset+lengthOflength+ll])
		offset += lengthOflength + ll
	}

	return tag, value, nil
}

func (p *TLVParser) rtag() uint16 {
	return binary.BigEndian.Uint16(p.buf[:lengthOfTag])
}

func (p *TLVParser) rlen() uint32 {
	return binary.BigEndian.Uint32(p.buf[lengthOfTag:lengthOfHeader])
}

func (p *TLVParser) ensureLen(l uint32) {
	if uint32(len(p.buf)) < lengthOfHeader+l {
		var newLen = util.NextPower2(uint64(lengthOfHeader + l))

		if newLen-lengthOfHeader > math.MaxUint32 {
			newLen = math.MaxUint32 + lengthOfHeader
		}
		buf := make([]byte, newLen)

		copy(buf, p.buf)
		p.buf = buf
	}
}

func (p *TLVParser) ensuresbuf(l uint32) {
	if uint32(len(p.sbuf)) < lengthOfHeader+l {
		var newLen = util.NextPower2(uint64(lengthOfHeader + l))

		if newLen-lengthOfHeader > math.MaxUint32 {
			newLen = math.MaxUint32 + lengthOfHeader
		}
		buf := make([]byte, newLen)

		copy(buf, p.buf)
		p.sbuf = buf
	}
}

func (p *TLVParser) Recv(d time.Duration) (tag uint16, value [][]byte, err error) {
	var deadline time.Time
	if d != 0 {
		deadline = time.Now().Add(d)
	}

	p.r.SetReadDeadline(deadline)

	//读取 tag 和 length
	_, err = io.ReadFull(p.r, p.buf[:lengthOfHeader])
	if err != nil {
		return
	}

	tag = binary.BigEndian.Uint16(p.buf[:lengthOfTag])

	l := binary.BigEndian.Uint32(p.buf[lengthOfTag:lengthOfHeader])

	//扩容
	p.ensureLen(l)

	//读取 value
	p.r.SetReadDeadline(deadline)
	_, err = io.ReadFull(p.r, p.buf[lengthOfHeader:lengthOfHeader+l])
	if err != nil {
		return
	}

	//解析 value
	var offset uint32 = lengthOfHeader
	for offset < l+lengthOfHeader {
		ll := binary.BigEndian.Uint32(p.buf[offset : offset+lengthOflength])
		value = append(value, p.buf[offset+lengthOflength:offset+lengthOflength+ll])
		offset += lengthOflength + ll
	}

	return
}

func (p *TLVParser) Send(d time.Duration, tag uint16, value ...[]byte) error {
	var l int
	for _, v := range value {
		l += len(v) + lengthOflength
	}
	p.ensuresbuf(uint32(l))

	binary.BigEndian.PutUint16(p.sbuf[:lengthOfTag], tag)
	binary.BigEndian.PutUint32(p.sbuf[lengthOfTag:lengthOfHeader], uint32(l))

	var offset = lengthOfHeader
	for _, v := range value {
		binary.BigEndian.PutUint32(p.sbuf[offset:offset+lengthOflength], uint32(len(v)))
		copy(p.sbuf[offset+lengthOflength:], v)
		offset += lengthOflength + len(v)
	}

	binary.BigEndian.PutUint32(p.sbuf[lengthOfTag:lengthOfHeader], uint32(offset)-lengthOfHeader)

	var deadline time.Time
	if d != 0 {
		deadline = time.Now().Add(d)
	}
	p.r.SetWriteDeadline(deadline)
	_, err := p.r.Write(p.sbuf[:l+lengthOfHeader])
	return err
}

func (p *TLVParser) SetReadDeadLine(d time.Duration) error {
	var deadline time.Time
	if d != 0 {
		deadline = time.Now().Add(d)
	}

	return p.r.SetReadDeadline(deadline)
}

func (p *TLVParser) SetWriteDeadLine(d time.Duration) error {
	var deadline time.Time
	if d != 0 {
		deadline = time.Now().Add(d)
	}

	return p.r.SetWriteDeadline(deadline)
}

func (p *TLVParser) SetDeadLine(d time.Duration) error {
	var deadline time.Time
	if d != 0 {
		deadline = time.Now().Add(d)
	}

	return p.r.SetDeadline(deadline)
}

func (p *TLVParser) Write(b []byte) (int, error) {
	return p.r.Write(b)
}

func (p *TLVParser) Close() error {
	return p.r.Close()
}

func (p *TLVParser) Conn() net.Conn {
	return p.r
}

func (p *TLVParser) RemoteAddr() net.Addr {
	return p.r.RemoteAddr()
}
